<template>

  <el-container>
    <el-header>
      <el-row>
        <el-col :span="6">
          <div class="grid-content ep-bg-purple">名称：{{ ds.datasourceName }}</div>
        </el-col>
        <el-col :span="6">
          <div class="grid-content ep-bg-purple-light">数据库类型：{{ ds.dbTypeCode }}</div>
        </el-col>
        <el-col :span="6">
          <div class="grid-content ep-bg-purple">数据库:{{ ds.database }}</div>
        </el-col>
        <el-col :span="6">
          <div class="grid-content ep-bg-purple-light">ip:{{ ds.ip }}</div>
        </el-col>
      </el-row>
    </el-header>
    <el-main>

      <el-container>
        <el-aside width="200px">

          <div style="border: 1px solid black;height: 700px;overflow-x:scroll;width: 99%;">
            <el-tree :props="defaultProps" lazy :load="loadNode" style="width: 400px;overflow-x: scroll"
                     :expand-on-click-node="false" @node-click="handleNodeClick">

              <template #default="{ node, data }">
                       <span class="custom-tree-node" slot-scope="{ node }">
      <span @dblclick="dblClickItem">{{ node.label }}</span>
    </span>
              </template>
            </el-tree>

          </div>

        </el-aside>
        <el-main style="padding-top:0px;">
          <div style="border: 1px solid black;height: 700px;width: 100%;">

            <el-container>
              <el-header>
                <div style="padding-top: 6px;">
                  <el-row>
                    <el-col :span="6">
                      <div class="grid-content ep-bg-purple">&nbsp;</div>
                    </el-col>
                    <el-col :span="6">
                      <div class="grid-content ep-bg-purple-light">&nbsp;</div>
                    </el-col>
                    <el-col :span="8">
                      <div class="grid-content ep-bg-purple">&nbsp;</div>
                    </el-col>
                    <el-col :span="4">
                      <div class="grid-content ep-bg-purple-light">
                        <el-button type="default" size="small" @click="run()">运行</el-button>
                        <el-button type="default" size="small" @click="explain()">执行计划</el-button>

                      </div>
                    </el-col>
                  </el-row>
                </div>


              </el-header>
              <el-main style="padding-top: 0px;">
                <div class="codemirror">
                  <div style="height: 410px;text-align: left;" id="monacoEditor" ref="monacoEditor"
                       class="monaco-editor"/>
                </div>
              </el-main>
              <el-footer>

                <el-tabs v-model="activeName" class="demo-tabs">
                  <el-tab-pane label="结果集" name="resultSet" style="overflow-x: scroll">
                    <el-table class="tableClass" :data="tableData" style="width: 100%;" height="146px"
                              :fit="true" :stripe="true" :border="true" size:="large" :highlight-current-row="true">
                      <el-table-column type="index" width="50"/>
                      <el-table-column v-for="(item, index) in columns" :prop="item" :label="item" :key="index"
                                       :render-header="renderHeader" show-overflow-tooltip>
                      </el-table-column>
                    </el-table>


                  </el-tab-pane>
                  <el-tab-pane label="执行日志" name="executeLog">{{ executeLog }}</el-tab-pane>
                </el-tabs>

              </el-footer>
            </el-container>
          </div>

        </el-main>
      </el-container>

    </el-main>
  </el-container>

</template>

<script setup>
import * as monaco from 'monaco-editor'
import {language} from 'monaco-editor/esm/vs/basic-languages/sql/sql'
import {onMounted, onBeforeUnmount, ref, getCurrentInstance, h} from 'vue'
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
import apis from "@/request/apis.js";
import {ElMessage} from "element-plus";

const activeName = ref("resultSet");
const executeLog = ref("");
const columns = ref([]);
const tableData = ref([]);
self.MonacoEnvironment = {
  getWorker(workerId, label) {
    return new editorWorker()
  }
}

const renderHeader = ({column, $index}) => {
  // 新建一个 span
  let span = document.createElement('span');
  // 设置表头名称
  span.innerText = column.label;
  // 临时插入 document
  document.body.appendChild(span);
  // 重点：获取 span 最小宽度，设置当前列，注意这里加了 20，字段较多时还是有挤压，且渲染后的 div 内左右 padding 都是 10，所以 +20 。（可能还有边距/边框等值，需要根据实际情况加上）
  column.minWidth = span.getBoundingClientRect().width + 30;
  // 移除 document 中临时的 span
  document.body.removeChild(span);
  return h('span', column.label);
};

const code = ref('') // 代码

// 获取 SQL 的关键字
const {keywords} = language
let editor

// 初始化 SQL 代码和表格数据
//只能提示需要补全
const tables = {}

const ds = ref({});

// 编辑器的主题设置
const theme = 'vs-dark';

const defaultProps = ref({
  children: 'children',
  label: 'label',
  name: "name",
  isLeaf: 'isLeaf'
})

const {proxy} = getCurrentInstance();

const currentNode = ref();
const handleNodeClick = (node) => {
  currentNode.value = node;
};

const supportsSchemas = ref(false);

const dblClickItem = () => {
  if (!currentNode.value) {
    return;
  }
  if (!currentNode.value.type) {
    return;
  }

  setTextByCursor(currentNode.value.name);
  if (currentNode.value.type != "table") {
    return;
  }
  queryTableDdl(currentNode.value.name);
};

const setTextByCursor = (insertText) => {
  // manacoEditor 为 实例对象
// 1 获取光标位置
  const position = editor.getPosition()
// 2 插入
  editor.executeEdits('', [
    {
      range: new monaco.Range(position.lineNumber,
          position.column,
          position.lineNumber,
          position.column),
      text: insertText
    }
  ])
// 3 设置新的光标位置
//   editor.setPosition(position.lineNumber, position.column + insertText.length)
// 4 聚焦
  editor.focus();
}


const run = async () => {

  var value = editor.getValue();
  var sb = editor.getModel().getValueInRange(editor.getSelection());
  if (sb) {
    value = sb;
  }
  var s = await apis.post("/square-lake/process/datasource/run", {
    id: proxy.$route.query.id,
    sql: value
  });
  if (s) {
    columns.value = s.headers;
    setTimeout(() => {
      tableData.value = s.rows;
    }, 0)

    var str = "";
    if (s.message) {
      str += s.message + "\n";
    }
    str += "执行完成,耗时" + s.time + "ms";
    executeLog.value = str;
  }
};
const explain = async () => {

  var value = editor.getValue();
  var sb = editor.getModel().getValueInRange(editor.getSelection());
  if (sb) {
    value = sb;
  }

  value = "explain " + value;
  var s = await apis.post("/square-lake/process/datasource/run", {
    id: proxy.$route.query.id,
    sql: value
  });
  if (s) {

    columns.value = s.headers;
    setTimeout(() => {
      tableData.value = s.rows;
    }, 0);

    var str = "";
    if (s.message) {
      str += s.message + "\n";
    }
    str += "执行完成,耗时" + s.time + "ms";
    executeLog.value = str;
  }
};

const queryTableDdl = async (tableName) => {
  var d = await apis.post("/square-lake/process/datasource/query_table_ddl", {
    id: proxy.$route.query.id,
    tableName: tableName
  });
  ElMessage({
    dangerouslyUseHTMLString: true,
    type: "success",
    message: d
  });
};
const loadNode = async (node, resolve) => {
  if (node.level === 0) {
    return resolve([{label: '全部'}])

  }
  if (node.level === 1) {
    let data = await queryCatalogList();
    if (data) {
      resolve(data)
    }
  }

  if (node.level === 2) {
    //先查询schema

    let data = await querySchemaList(node.data.name);
    if (data && data.length > 0) {
      supportsSchemas.value = true;
      resolve(data)
    } else {
      supportsSchemas.value = false;
      let data = await queryTableList(node.data.name);
      if (data) {
        resolve(data)
      }
    }

  }
  if (node.level === 3) {
    if (supportsSchemas.value) {
      var data = await queryTableList(node.data.name, node.parent.data.name);
      if (data) {
        resolve(data)
      }
    } else {
      var data = await queryTableFieldList(node.data.name, node.parent.data.name);
      if (data) {
        resolve(data)
      }
    }
  }
  if (node.level === 4) {
    if (supportsSchemas.value) {
      var data = await queryTableFieldList(node.data.name, node.parent.data.name);
      if (data) {
        resolve(data)
      }
    }
  }
  if (node.level > 3 && !supportsSchemas.value) return resolve([])
  if (node.level > 4) return resolve([])
}

const queryTableFieldList = async (tableName, catalogName) => {
  //查询表
  var s = await apis.post("/square-lake/process/datasource/query_table_field_list", {
    id: proxy.$route.query.id,
    catalogName: catalogName,
    tableName: tableName
  });
  if (s) {
    var d = [];
    s.forEach(v => {
      d.push({
        name: v.columnName,
        label: v.columnName + " " + v.columnType + "(" + v.columnLength + ")",
        isLeaf: true,
        type: "column",
        children: []
      })

    })
    return d;
  }
  return [];
};

const queryTableList = async (catalogName) => {
  //查询表
  var s = await apis.post("/square-lake/process/datasource/query_table_list", {
    id: proxy.$route.query.id,
    catalogName: catalogName
  });
  if (s) {
    var d = [];
    s.forEach(v => {
      d.push({
        name: v,
        label: v,
        isLeaf: false,
        type: "table",
        children: []
      })

    })
    return d;
  }
  return [];

};

const queryCatalogList = async () => {
  var s = await apis.post("/square-lake/process/datasource/query_catalog_list", {
    id: proxy.$route.query.id
  });
  if (s) {
    var d = [];
    s.forEach(v => {
      d.push({
        name: v,
        label: v,
        isLeaf: false,
        type: "catalog",
        children: []
      })

    })
    return d;
  }
  return [];
};

const querySchemaList = async () => {
  var s = await apis.post("/square-lake/process/datasource/query_schema_list", {
    id: proxy.$route.query.id
  });
  if (s) {
    var d = [];
    s.forEach(v => {
      d.push({
        name: v,
        label: v,
        isLeaf: false,
        type: "schema",
        children: []
      })

    })
    return d;
  }
  return [];
};

const queryById = async () => {
  var config = await apis.post("/square-lake/process/datasource/get_by_id", {
    id: proxy.$route.query.id
  });
  ds.value = config;
}

// 组件挂载后创建编辑器实例
onMounted(async () => {

  //查询数据库详情
  await queryById();

  //初始化自动完成信息
  initAutoCompletion();

  editor = monaco.editor.create(document.getElementById('monacoEditor'), {
    value: 'select * from dual',
    language: 'sql',
    readOnly: false,
    automaticLayout: true,
    colorDecorators: true, // 颜色装饰器
    theme: theme,
    minimap: {
      enabled: false
    },
    tabSize: 2,
    fontSize: 16
  })
})
// 组件卸载前销毁编辑器实例
onBeforeUnmount(() => {
  if (editor) {
    editor.dispose()
  }
})

/**
 * @description: 获取编辑器中填写的值
 */
function getValue() {
  return editor.getValue()
}

/**
 * @description: 初始化自动补全
 */
function initAutoCompletion() {
  monaco.languages.registerCompletionItemProvider('sql', {
    triggerCharacters: ['.', ' ', ...keywords],
    provideCompletionItems: (model, position) => {
      let suggestions = []
      const {lineNumber, column} = position
      const textBeforePointer = model.getValueInRange({
        startLineNumber: lineNumber,
        startColumn: 0,
        endLineNumber: lineNumber,
        endColumn: column
      })
      const words = textBeforePointer.trim().split(/\s+/)
      const lastWord = words[words.length - 1]

      if (lastWord.endsWith('.')) {
        const tableName = lastWord.slice(0, lastWord.length - 1)
        if (Object.keys(tables).includes(tableName)) {
          suggestions = [...getFieldsSuggest(tableName)]
        }
      } else if (lastWord === '.') {
        suggestions = []
      } else {
        suggestions = [...getTableSuggest(), ...getKeywordsSuggest()]
      }
      return {
        suggestions
      }
    }
  })
}

/**
 * @description: 获取关键字的补全列表
 *
 */
function getKeywordsSuggest() {
  return keywords.map((key) => ({
    label: key,
    kind: monaco.languages.CompletionItemKind.Keyword,
    insertText: key
  }))
}

/**
 * @description: 获取表名的补全列表
 */
function getTableSuggest() {
  return Object.keys(tables).map((key) => ({
    label: key,
    kind: monaco.languages.CompletionItemKind.Variable,
    insertText: key
  }))
}

/**
 * @description: 根据表名获取字段补全列表
 * @param {*} tableName
 */
function getFieldsSuggest(tableName) {
  const fields = tables[tableName]
  if (!fields) {
    return []
  }
  return fields.map((name) => ({
    label: name,
    kind: monaco.languages.CompletionItemKind.Field,
    insertText: name
  }))
}


</script>

<style scoped>
</style>